package middleware

import (
	"errors"
	"fmt"
	"gitee.com/zhancaihua/goyt/core/logyt"
	"github.com/gin-gonic/gin"
	"net"
	"net/http"
	"net/http/httputil"
	"os"
	"strings"
)

// RecoveryFunc defines the function passable to CustomRecovery.
type RecoveryFunc func(c *gin.Context, err any)

// RecoveryWithHandler returns a middleware that recovers from any panics and writes a 500 if there was one.
func RecoveryWithHandler(handle RecoveryFunc) gin.HandlerFunc {
	return CustomRecovery(RecoveryConfig{Handle: handle})
}

// Recovery returns a middleware that recovers from any panics and calls the provided handle func to handle it.
func Recovery() gin.HandlerFunc {
	return CustomRecovery(RecoveryConfig{Handle: defaultHandleRecovery})
}

type RecoveryConfig struct {
	Handle RecoveryFunc
}

// CustomRecovery returns a middleware for a given writer that recovers from any panics and calls the provided handle func to handle it.
func CustomRecovery(conf RecoveryConfig) gin.HandlerFunc {
	return func(c *gin.Context) {
		defer func() {
			if err := recover(); err != nil {
				// Check for a broken connection, as it is not really a
				// condition that warrants a panic stack trace.
				var brokenPipe bool
				if ne, ok := err.(*net.OpError); ok {
					var se *os.SyscallError
					if errors.As(ne, &se) {
						seStr := strings.ToLower(se.Error())
						if strings.Contains(seStr, "broken pipe") ||
							strings.Contains(seStr, "connection reset by peer") {
							brokenPipe = true
						}
					}
				}
				httpRequest, _ := httputil.DumpRequest(c.Request, false)
				headers := strings.Split(string(httpRequest), "\r\n")
				for idx, header := range headers {
					current := strings.Split(header, ":")
					if current[0] == "Authorization" {
						headers[idx] = current[0] + ": *"
					}
				}
				headersToStr := strings.Join(headers, "\r\n")
				if brokenPipe {
					if logyt.IsTerm() {
						logyt.ErrorLog(fmt.Sprintf("%s\n%s%s", err, headersToStr, reset))
					} else {
						logyt.ErrorLog(fmt.Sprintf("%s\n%s", err, headersToStr))
					}
				} else if gin.IsDebugging() {
					if logyt.IsTerm() {
						logyt.ErrorLog(fmt.Sprintf("[Recovery] panic recovered:\n%s\n%s\n%s",
							headersToStr, err, reset))
					} else {
						logyt.ErrorLog(fmt.Sprintf("[Recovery] panic recovered:\n%s\n%s",
							headersToStr, err))
					}
				} else {
					if logyt.IsTerm() {
						logyt.ErrorLog(fmt.Sprintf("[Recovery] panic recovered:\n%s%s",
							err, reset))
					} else {
						logyt.ErrorLog(fmt.Sprintf("[Recovery] panic recovered:\n%s",
							err))
					}

				}
				if brokenPipe {
					// If the connection is dead, we can't write a status to it.
					c.Error(err.(error)) //nolint: errcheck
					c.Abort()
				} else { //只要不是网络错误连接出问题，就还可以继续响应
					if conf.Handle != nil {
						conf.Handle(c, err)
					}
				}
			}
		}()
		c.Next()
	}
}

func defaultHandleRecovery(c *gin.Context, err any) {
	c.AbortWithStatus(http.StatusInternalServerError)
}
